function As = slsymgraph(A, symmethod)
%SLSYMGRAPH Forces symmetry of the adjacency matrix of a graph
%
% [ Syntax ]
%   - As = slsymgraph(A)
%   - As = slsymgraph(A, symmethod)
%
% [ Arguments ]
%   - A:            The adjacency matrix of the original graph
%   - symmethod:    The method to symmetrize the graph
%   - As:           The symmetry adjacency matrix
% 
% [ Description ]
%   - As = slsymgraph(A) makes a symmetry version of the adjacency matrix
%     A using default method.(Please refer to the avgor option in the next
%     item).
%
%   - As = slsymgraph(A, symmethod) symmetrizes the adjacency matrix by 
%     using the specified method. 
%
%     Here, symmethod can be either a string selecting the predefined
%     schemes or a function handle. Here are the predefined ones.
%     \[
%       &!     The method to symmetrizes adjacency matrix
%       &^       name      &^         description 
%       & --------------------------------------------------------------
%       &      avgor       & Force symmetry using the following rule:
%       |                  |  - if both aij and aji are non-zeros, then 
%       |                  |    take their average
%       |                  |  - if only one of aij and aji is non-zero, then
%       |                  |    take the non-zero one
%       |                  |  - if both aij and aji are zeros, then set zero
%       |                  |    (for both logical and numeric)
%       &      avgand      & Force symmetry using the following rule:
%       |                  |  - if both aij and aji are non-zeros, then 
%       |                  |    take their average
%       |                  |  - if either one of aij and aji is zero, then
%       |                  |    set zero (for both logical and numeric)
%       &      or          & Use or-rule: d = aij | aji
%       &      and         & Use and-rule: d = aij & aji
%       &      simavg      & make simple average: always take (aij+aji)/2
%     \]
%     The default method to use is 'avgor'. 
%
%     If you supply your scheme using function handle f, which should
%     support the following form:
%        $ v = f(v1, v2) $
%     v1 and v2 are column vectors of equal size. If v1(k) is A(i, j) then
%     v2(k) would be A(j, i). At least one of v1(k) and v2(k) is non-zero.
%     For a reasonable function, it should satisfy the condition that
%        $ f(v, v) == v && f(v1, v2) = f(v2, v1) $.
%     This guarantees the consistency of the processed graph.
%
% [ Remarks ]
%   - A can be full matrix or sparse matrix, and As preserves the same 
%     storage form.
%
%   - A should be a square matrix.
%
%   - When 'avgor' method is applied to logical, it is equivalent to 'or'
%     when 'avgand' method is applied to logical, it is equivalent to
%     'and'.
%
% [ History ]
%   - Created by Dahua Lin, on Sep 8, 2006
%   - Modified by Dahua Lin, on Jul 5, 2007
%       - simplify the internal algorithm
%

%% parse and verify input

if nargin < 2 || isempty(symmethod)
    symmethod = 'avgor';
end

[n, f] = parse_inputs(A, symmethod);
            

%% main 

As = A;

[I, J, V] = find(A);

not_diag = (I ~= J);
I = I(not_diag);
if ~isempty(I)

    J = J(not_diag);
    V = V(not_diag);
    
    siz = size(A);
    Inds1 = sub2ind(siz, I, J);
    Inds2 = sub2ind(siz, J, I);
    V2 = A(Inds2);

    Vals = f(V, V2);

    As(Inds1) = Vals;
    As(Inds2) = Vals;
end


%% Input parsing

function [n, f] = parse_inputs(A, symmethod)

n = size(A, 1);
if ndims(A) ~= 2 || size(A, 2) ~= n || (~isnumeric(A) && ~islogical(A))
    error('sltoolbox:slsymgraph:invalidarg', ...
        'A should be a square 2D numeric/logical matrix');
end

if ischar(symmethod)
    switch symmethod
        case 'avgor'
            f = @(v1, v2) (v1 + v2) ./ (1 + (v1 & v2));
        case 'avgand'
            f = @(v1, v2) (v1 + v2) .* (v1 & v2) * 0.5;
        case 'or'
            f = @(v1, v2) v1 | v2;            
        case 'and'
            f = @(v1, v2) v1 & v2;
        case 'simavg'
            f = @(v1, v2) (v1 + v2) * 0.5;
        otherwise
            error('sltoolbox:slsymgraph:invalidopts', ...
                'Invalid symmethod: %s', method);
    end
elseif isa(symmethod, 'function_handle')
    f = symmethod;
else
    error('sltoolbox:slsymgraph:invalidarg', ...
        'The symmethod should be either a string or a function handle');
end





